//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package project_requirement_api_serv

import (
	"context"
	"time"

	"atomgit.com/openlinksaas-org/api-server/api_common"
	"atomgit.com/openlinksaas-org/api-server/dao"
	"atomgit.com/openlinksaas-org/api-server/dao/issue_dao"
	"atomgit.com/openlinksaas-org/api-server/dao/requirement_dao"
	"atomgit.com/openlinksaas-org/api-server/utils"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/notices/notices_requirement"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_issue_api"
	"atomgit.com/openlinksaas-org/proto-gen-go.git/project_requirement_api"
	"go.mongodb.org/mongo-driver/mongo"
)

func (apiImpl *ProjectRequirementApiImpl) LinkIssue(ctx context.Context, req *project_requirement_api.LinkIssueRequest) (*project_requirement_api.LinkIssueResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	//检查关联是否存在
	exist, err := requirement_dao.LinkIssueInfoDao.Exist(ctx, req.IssueId, "")
	if err != nil {
		return nil, err
	}
	if exist {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_HAS_ISSUE_LINK,
			ErrMsg: "工单已关联到需求",
		}, nil
	}
	//检查需求是否存在
	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if !(sessionItem.UserId == projectItem.OwnerUserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId) {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}
	//检查工单是否存在
	issueItem, err := issue_dao.IssueDao.Get(ctx, req.ProjectId, req.IssueId)
	if err != nil {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_NO_ISSUE,
			ErrMsg: "工单不存在",
		}, nil
	}
	if issueItem.IssueType != int32(project_issue_api.ISSUE_TYPE_ISSUE_TYPE_TASK) {
		return &project_requirement_api.LinkIssueResponse{
			Code:   project_requirement_api.LinkIssueResponse_CODE_NOT_ISSUE_TASK,
			ErrMsg: "只能关联任务类型的工单",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.LinkIssueInfoDao.Insert(sessCtx, &requirement_dao.LinkIssueInfoDbItem{
			IssueId:       req.IssueId,
			ProjectId:     req.ProjectId,
			RequirementId: req.RequirementId,
			TimeStamp:     nowTime,
		})
		if err != nil {
			return nil, err
		}
		count, err := requirement_dao.LinkIssueInfoDao.Count(sessCtx, req.ProjectId, req.RequirementId)
		if err != nil {
			return nil, err
		}
		err = requirement_dao.RequirementInfoDao.UpdateIssueLinkCount(sessCtx, req.ProjectId, req.RequirementId, count)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.LinkIssueResponse{
			Code: project_requirement_api.LinkIssueResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addLinkIssueEvent(sessionItem, projectItem, reqItem, issueItem, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.LinkIssueNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
		IssueId:       req.IssueId,
	})

	return ret.(*project_requirement_api.LinkIssueResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) UnlinkIssue(ctx context.Context, req *project_requirement_api.UnlinkIssueRequest) (*project_requirement_api.UnlinkIssueResponse, error) {
	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_WRONG_SESSION,
			ErrMsg: "会话不存在",
		}, nil
	}
	projectItem, _, roleItem, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_NO_PROJECT,
			ErrMsg: "项目不存在",
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	} else if err != nil {
		return nil, err
	}

	//检查关联是否存在
	exist, err := requirement_dao.LinkIssueInfoDao.Exist(ctx, req.IssueId, req.RequirementId)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_NO_ISSUE_LINK,
			ErrMsg: "工单关联不存在",
		}, nil
	}
	//检查需求是否存在
	reqItem, err := requirement_dao.RequirementInfoDao.Get(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg: "需求不存在",
		}, nil
	}
	if !(sessionItem.UserId == projectItem.OwnerUserId || roleItem.BasicInfo.Admin || sessionItem.UserId == reqItem.CreateUserId) {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_NO_PERMISSION,
			ErrMsg: "没有权限",
		}, nil
	}

	//检查工单是否存在
	issueItem, err := issue_dao.IssueDao.Get(ctx, req.ProjectId, req.IssueId)
	if err != nil {
		return &project_requirement_api.UnlinkIssueResponse{
			Code:   project_requirement_api.UnlinkIssueResponse_CODE_NO_ISSUE,
			ErrMsg: "工单不存在",
		}, nil
	}

	nowTime := time.Now().UnixNano() / 1e6

	dbSess, err := dao.StartSession()
	if err != nil {
		return nil, err
	}
	defer dao.EndSession(dbSess)

	ret, err := dbSess.WithTransaction(ctx, func(sessCtx mongo.SessionContext) (interface{}, error) {
		err = requirement_dao.LinkIssueInfoDao.Remove(sessCtx, req.IssueId, req.RequirementId)
		if err != nil {
			return nil, err
		}
		count, err := requirement_dao.LinkIssueInfoDao.Count(sessCtx, req.ProjectId, req.RequirementId)
		if err != nil {
			return nil, err
		}
		err = requirement_dao.RequirementInfoDao.UpdateIssueLinkCount(sessCtx, req.ProjectId, req.RequirementId, count)
		if err != nil {
			return nil, err
		}
		return &project_requirement_api.UnlinkIssueResponse{
			Code: project_requirement_api.UnlinkIssueResponse_CODE_OK,
		}, nil
	})
	if err != nil {
		return nil, err
	}
	//记录事件
	go apiImpl.addUnlinkIssueEvent(sessionItem, projectItem, reqItem, issueItem, nowTime)
	//发送通知
	go api_common.SendNoticeToProjectScope(context.Background(), req.ProjectId, &notices_requirement.UnlinkIssueNotice{
		ProjectId:     req.ProjectId,
		RequirementId: req.RequirementId,
		IssueId:       req.IssueId,
	})
	return ret.(*project_requirement_api.UnlinkIssueResponse), nil
}

func (apiImpl *ProjectRequirementApiImpl) ListIssueLink(ctx context.Context, req *project_requirement_api.ListIssueLinkRequest) (*project_requirement_api.ListIssueLinkResponse, error) {
	emptyList := []string{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.ListIssueLinkResponse{
			Code:        project_requirement_api.ListIssueLinkResponse_CODE_WRONG_SESSION,
			ErrMsg:      "会话不存在",
			IssueIdList: emptyList,
		}, nil
	}
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.ListIssueLinkResponse{
			Code:        project_requirement_api.ListIssueLinkResponse_CODE_NO_PROJECT,
			ErrMsg:      "项目不存在",
			IssueIdList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.ListIssueLinkResponse{
			Code:        project_requirement_api.ListIssueLinkResponse_CODE_NO_PERMISSION,
			ErrMsg:      "没有权限",
			IssueIdList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	//检查需求是否存在
	exist, err := requirement_dao.RequirementInfoDao.Exist(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return nil, err
	}
	if !exist {
		return &project_requirement_api.ListIssueLinkResponse{
			Code:        project_requirement_api.ListIssueLinkResponse_CODE_NO_REQUIRE_MENT,
			ErrMsg:      "需求不存在",
			IssueIdList: emptyList,
		}, nil
	}
	linkItemList, err := requirement_dao.LinkIssueInfoDao.List(ctx, req.ProjectId, req.RequirementId)
	if err != nil {
		return nil, err
	}
	issueIdList := []string{}
	for _, linkItem := range linkItemList {
		issueIdList = append(issueIdList, linkItem.IssueId)
	}
	return &project_requirement_api.ListIssueLinkResponse{
		Code:        project_requirement_api.ListIssueLinkResponse_CODE_OK,
		IssueIdList: issueIdList,
	}, nil
}

func (apiImpl *ProjectRequirementApiImpl) ListMultiIssueLink(ctx context.Context, req *project_requirement_api.ListMultiIssueLinkRequest) (*project_requirement_api.ListMultiIssueLinkResponse, error) {
	emptyList := []string{}

	sessionItem, ok := dao.CacheDao.GetSession(ctx, req.SessionId)
	if !ok {
		return &project_requirement_api.ListMultiIssueLinkResponse{
			Code:        project_requirement_api.ListMultiIssueLinkResponse_CODE_WRONG_SESSION,
			ErrMsg:      "会话不存在",
			IssueIdList: emptyList,
		}, nil
	}
	_, _, _, err := utils.GetProjectMemberRole(ctx, req.ProjectId, sessionItem.UserId)
	if err == utils.ErrNoProject {
		return &project_requirement_api.ListMultiIssueLinkResponse{
			Code:        project_requirement_api.ListMultiIssueLinkResponse_CODE_NO_PROJECT,
			ErrMsg:      "项目不存在",
			IssueIdList: emptyList,
		}, nil
	} else if err == utils.ErrNoRole {
		return &project_requirement_api.ListMultiIssueLinkResponse{
			Code:        project_requirement_api.ListMultiIssueLinkResponse_CODE_NO_PERMISSION,
			ErrMsg:      "没有权限",
			IssueIdList: emptyList,
		}, nil
	} else if err != nil {
		return nil, err
	}

	linkItemList, err := requirement_dao.LinkIssueInfoDao.ListMulti(ctx, req.ProjectId, req.RequirementIdList)
	if err != nil {
		return nil, err
	}
	issueIdList := []string{}
	for _, linkItem := range linkItemList {
		issueIdList = append(issueIdList, linkItem.IssueId)
	}
	return &project_requirement_api.ListMultiIssueLinkResponse{
		Code:        project_requirement_api.ListMultiIssueLinkResponse_CODE_OK,
		IssueIdList: issueIdList,
	}, nil
}
